package com.yhkj.data.mq.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StreamUtils;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.JDBCType;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author <a href="mailto:idler41@163.con">linfuxin</a> created on 2023-03-03 20:46:50
 */
@Slf4j
@AllArgsConstructor
@Getter
public enum JdbcMessageEnum {
    // 基础类型与包装类 序列化不需要额外操作
    STRING(String.class),
    BYTE(Byte.class) {
        @Override
        public Byte parseValue(String value) {
            return value == null ? null : new Byte(value);
        }
    },
    SHORT(Short.class) {
        @Override
        public Short parseValue(String value) {
            return value == null ? null : new Short(value);
        }
    },
    INTEGER(Integer.class) {
        @Override
        public Integer parseValue(String value) {
            return value == null ? null : Integer.parseInt(value);
        }
    },
    LONG(Long.class) {
        @Override
        public Long parseValue(String value) {
            return value == null ? null : Long.parseLong(value);
        }
    },
    FLOAT(Float.class) {
        @Override
        public Float parseValue(String value) {
            return value == null ? null : new Float(value);
        }
    },
    DOUBLE(Double.class) {
        @Override
        public Double parseValue(String value) {
            return value == null ? null : new Double(value);
        }
    },
    BIG_DECIMAL(BigDecimal.class) {
        @Override
        public BigDecimal parseValue(String value) {
            return value == null ? null : new BigDecimal(value);
        }

        @Override
        public String toValue(Object data) {
            return data == null ? null : data.toString();
        }
    },
    DATE(Date.class) {
        @Override
        public Date parseValue(String value) {
            return value == null ? null : new Date(Long.parseLong(value));
        }

        @Override
        public Long toValue(Object data) {
            return data == null ? null : ((Date) data).getTime();
        }
    },
    TIME(Time.class) {
        @Override
        public Time parseValue(String value) {
            return value == null ? null : new Time(Long.parseLong(value));
        }

        @Override
        public Long toValue(Object data) {
            return data == null ? null : ((Time) data).getTime();
        }
    },
    TIMESTAMP(Timestamp.class) {
        @Override
        public Timestamp parseValue(String value) {
            return value == null ? null : new Timestamp(Long.parseLong(value));
        }

        @Override
        public Long toValue(Object data) {
            return data == null ? null : ((Timestamp) data).getTime();
        }
    },
    BLOB(Blob.class) {
        @Override
        public byte[] parseValue(String value) {
            return value == null ? null : Base64.getDecoder().decode(value);
        }

        @Override
        public String toValue(Object data) {
            if (data == null) {
                return null;
            }
            try (BufferedInputStream in = new BufferedInputStream(((Blob) data).getBinaryStream())) {
                return Base64.getEncoder().encodeToString(StreamUtils.copyToByteArray(in));
            } catch (SQLException | IOException e) {
                throw new RuntimeException(e);
            }
        }
    },

    CLOB(Clob.class) {
        @Override
        public byte[] parseValue(String value) {
            return value == null ? null : Base64.getDecoder().decode(value);
        }

        @Override
        public String toValue(Object data) {
            if (data == null) {
                return null;
            }
            try (BufferedInputStream in = new BufferedInputStream(((Clob) data).getAsciiStream())) {
                return Base64.getEncoder().encodeToString(StreamUtils.copyToByteArray(in));
            } catch (SQLException | IOException e) {
                throw new RuntimeException(e);
            }
        }
    },
    BINARY(byte[].class) {
        @Override
        public byte[] parseValue(String value) {
            return value == null ? null : Base64.getDecoder().decode(value);
        }

        @Override
        public String toValue(Object data) {
            if (data == null) {
                return null;
            }
            return Base64.getEncoder().encodeToString((byte[]) data);
        }
    },
    ;

    private final Class<?> javaType;

    public Object parseValue(String value) {
        // todo 改为Object参数接收参数，反序列化时传入对应值即可
        return value;
    }

    public Object toValue(Object data) {
        return data;
    }

    public static JdbcMessageEnum getInstance(int jdbcType) {
        Class<?> clazz = JDBC_JAVA_MAP.get(jdbcType);
        if (clazz != null) {
            return INSTANCE_MAP.get(clazz.getName() + jdbcType);
        }
        return null;
    }

    private static final Map<String, JdbcMessageEnum> INSTANCE_MAP;
    private static final Map<Integer, Class<?>> JDBC_JAVA_MAP;

    static {
        JDBC_JAVA_MAP = new HashMap<>(JDBCType.values().length);

        JDBC_JAVA_MAP.put(JDBCType.CHAR.getVendorTypeNumber(), String.class);
        JDBC_JAVA_MAP.put(JDBCType.VARCHAR.getVendorTypeNumber(), String.class);
        JDBC_JAVA_MAP.put(JDBCType.LONGVARCHAR.getVendorTypeNumber(), String.class);
        JDBC_JAVA_MAP.put(JDBCType.NCHAR.getVendorTypeNumber(), String.class);
        JDBC_JAVA_MAP.put(JDBCType.NVARCHAR.getVendorTypeNumber(), String.class);

        JDBC_JAVA_MAP.put(JDBCType.NUMERIC.getVendorTypeNumber(), BigDecimal.class);
        JDBC_JAVA_MAP.put(JDBCType.DECIMAL.getVendorTypeNumber(), BigDecimal.class);

        JDBC_JAVA_MAP.put(JDBCType.BOOLEAN.getVendorTypeNumber(), Boolean.class);
        JDBC_JAVA_MAP.put(JDBCType.BIT.getVendorTypeNumber(), Boolean.class);
        JDBC_JAVA_MAP.put(JDBCType.TINYINT.getVendorTypeNumber(), Byte.class);
        JDBC_JAVA_MAP.put(JDBCType.SMALLINT.getVendorTypeNumber(), Short.class);
        JDBC_JAVA_MAP.put(JDBCType.INTEGER.getVendorTypeNumber(), Integer.class);

        JDBC_JAVA_MAP.put(JDBCType.BIGINT.getVendorTypeNumber(), Long.class);
        JDBC_JAVA_MAP.put(JDBCType.REAL.getVendorTypeNumber(), Float.class);
        JDBC_JAVA_MAP.put(JDBCType.FLOAT.getVendorTypeNumber(), Float.class);
        JDBC_JAVA_MAP.put(JDBCType.DOUBLE.getVendorTypeNumber(), Double.class);

        JDBC_JAVA_MAP.put(JDBCType.DATE.getVendorTypeNumber(), Date.class);
        JDBC_JAVA_MAP.put(JDBCType.TIME.getVendorTypeNumber(), Time.class);
        JDBC_JAVA_MAP.put(JDBCType.TIMESTAMP.getVendorTypeNumber(), Timestamp.class);

        // 大对象可能超过传递消息大小限制，建议上传到文件系统，value改为文件地址。消费端重新下载文件
        JDBC_JAVA_MAP.put(JDBCType.BINARY.getVendorTypeNumber(), byte[].class);
        JDBC_JAVA_MAP.put(JDBCType.VARBINARY.getVendorTypeNumber(), byte[].class);
        // mysql blob类型 查询为JDBCType.LONGVARBINARY类型?
        JDBC_JAVA_MAP.put(JDBCType.LONGVARBINARY.getVendorTypeNumber(), byte[].class);
        JDBC_JAVA_MAP.put(JDBCType.BLOB.getVendorTypeNumber(), Blob.class);
        JDBC_JAVA_MAP.put(JDBCType.CLOB.getVendorTypeNumber(), Clob.class);

        INSTANCE_MAP = new HashMap<>(values().length);
        Map<Class<?>, JdbcMessageEnum> enumMap = Arrays.stream(values()).collect(Collectors.toMap(JdbcMessageEnum::getJavaType, v -> v));
        for (Map.Entry<Integer, Class<?>> entry : JDBC_JAVA_MAP.entrySet()) {
            Class<?> clazz = entry.getValue();
            String key = clazz.getName() + entry.getKey();
            INSTANCE_MAP.put(key, enumMap.get(clazz));
        }
    }

//    public static void main(String[] args) throws JsonProcessingException {
//        ObjectMapper objectMapper = new ObjectMapper();
//        System.out.println(Base64.encodeBase64String("123".getBytes()));
//        System.out.println(objectMapper.writeValueAsString("123".getBytes()));
//        System.out.println(objectMapper.writeValueAsString(new Date(System.currentTimeMillis())));
//        System.out.println(objectMapper.writeValueAsString(new Timestamp(System.currentTimeMillis())));
//        System.out.println(objectMapper.writeValueAsString(new Time(System.currentTimeMillis())));
//        System.out.println(objectMapper.writeValueAsString(new com.mysql.cj.jdbc.Blob("123".getBytes(), null)));
//        System.out.println(objectMapper.writeValueAsString(new com.mysql.cj.jdbc.Clob("123", null)));
//    }
}
